<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <title>批量自动注册面板 (Python Backend)</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- More responsive -->
  <style>
    body {
      background: #f5f7fa;
      font-family: 'Microsoft YaHei', Arial, sans-serif;
      margin: 0;
      padding: 20px; /* Add padding for smaller screens */
    }
    #main-panel {
      margin: 20px auto; /* Adjusted margin */
      background: #fff;
      border-radius: 8px;
      border: 2px solid #4b90ff;
      box-shadow: 0 4px 16px rgba(0,0,0,0.12);
      max-width: 1100px; /* Wider for more columns */
      padding: 25px 20px 15px 20px; /* Adjusted padding */
      font-size: 16px; /* Slightly smaller base font */
      line-height: 1.6em;
    }
    #main-panel h2 {
      color: #3576d1;
      margin-top: 0;
      margin-bottom: 20px;
      text-align: center;
    }
    .control-section {
        display: flex;
        flex-wrap: wrap; /* Allow wrapping on smaller screens */
        gap: 15px; /* Spacing between items */
        margin-bottom: 20px;
        align-items: center;
    }
    .field {
      display: flex;
      align-items: center;
      gap: 8px; /* Spacing within a field */
    }
    .field label {
      white-space: nowrap;
    }
    .field input[type="number"] {
      width: 70px;
      padding: 4px 6px;
      border: 1px solid #ccc;
      border-radius: 4px;
      font-size: 15px;
    }
    button {
      background: #4b90ff;
      border: 1px solid #3576d1;
      color: #fff;
      border-radius: 4px;
      padding: 5px 15px;
      cursor: pointer;
      font-size: 15px;
      transition: background-color 0.2s ease;
    }
    button:hover {
      background: #3576d1;
    }
    button:disabled {
      background: #a0c7ff;
      border-color: #79aae0;
      cursor: not-allowed;
      opacity: 0.7;
    }
    #clearFailedBtn { background: #ff9800; border-color: #e68a00; }
    #clearFailedBtn:hover { background: #e68a00; }
    #clearUsedBtn { background: #f44336; border-color: #d32f2f; }
    #clearUsedBtn:hover { background: #d32f2f; }

    #apiStatus.ok { color: #28a745; }
    #apiStatus.error { color: #dc3545; }
    #apiStatus.checking { color: #ffc107; }

    #summary {
        margin-bottom: 15px;
        font-size: 15px;
        color: #555;
        border-top: 1px solid #eee;
        padding-top: 15px;
    }

    #account-table-container {
        width: 100%;
        overflow-x: auto; /* Allow horizontal scroll on small screens */
        margin-bottom: 15px;
    }
    #account-table {
      /* margin: 15px 0; */ /* Removed margin as container handles spacing */
      width: 100%;
      border-collapse: collapse;
      background: #fff;
      min-width: 900px; /* Ensure minimum width for readability */
    }
    #account-table th, #account-table td {
      border: 1px solid #ddd;
      padding: 6px 10px; /* Increased padding */
      font-size: 13px;
      text-align: left;
      white-space: nowrap; /* Prevent wrapping in cells initially */
      vertical-align: top; /* Align content top */
    }
     #account-table td:nth-child(7) { /* Error message column */
        white-space: normal; /* Allow error message to wrap */
        min-width: 200px; /* Give error message more space */
        max-width: 350px; /* Limit max width */
        word-break: break-word;
    }
     #account-table td:nth-child(3), /* username */
     #account-table td:nth-child(2) { /* email */
        max-width: 200px;
        overflow: hidden;
        text-overflow: ellipsis;
     }
     #account-table td:nth-child(8) button { /* Action button */
        padding: 2px 8px;
        font-size: 12px;
     }

    #account-table th {
      background: #eef4ff;
      color: #333;
      position: sticky; /* Make header sticky */
      top: 0;
      z-index: 1;
    }
    /* Status Colors */
    .status-success { color: #28a745; font-weight: bold; }
    .status-fail { color: #dc3545; font-weight: bold; }
    .status-used { color: #fd7e14; }
    .status-pending { color: #6c757d; }

    #pagination {
      text-align: center;
      margin: 15px 0;
      display: flex; /* Use flexbox for alignment */
      justify-content: center;
      align-items: center;
    }
    #pagination button {
      background: #f8f9fa;
      border: 1px solid #dee2e6;
      margin: 0 4px;
      padding: 4px 10px;
      border-radius: 4px;
      color: #007bff;
    }
    #pagination button:hover { background: #e9ecef; }
    #pagination button:disabled {
      background: #e9ecef;
      color: #6c757d;
      cursor: not-allowed;
    }
    #pageInfo {
        margin: 0 15px;
        color: #555;
        font-size: 14px;
        white-space: nowrap;
    }

    #log-container { margin-top: 15px; }
    #log {
      max-height: 250px;
      overflow-y: auto;
      background: #f1f3f5;
      border: 1px solid #dee2e6;
      border-radius: 4px;
      padding: 10px 12px;
      font-size: 13px;
      color: #333;
      line-height: 1.6;
    }
    .log-line { margin-bottom: 4px; }
    .log-success { color: #28a745; }
    .log-fail { color: #dc3545; }
    .log-warn { color: #ffc107; }
    .log-info { color: #17a2b8; }
  </style>
</head>
<body>
  <div id="main-panel">
    <h2>批量自动注册面板 (Python Backend)</h2>

    <!-- Inside <div class="control-section"> -->
    <div class="control-section">
      <div class="field">
        <label for="regCount">批量注册数量:</label>
        <input id="regCount" type="number" min="1" max="1000" value="50">
      </div>
      <button id="startBatch">开始批量注册</button>

      <!-- Export Controls Group -->
      <div class="field" style="border-left: 1px solid #ccc; padding-left: 15px; margin-left: 10px;"> <!-- Group visually -->
          <label for="exportCount">导出数量:</label>
          <input id="exportCount" type="number" min="1" max="10000" value="50" style="width: 80px;"> <!-- Added input -->
          <button id="exportBtn">导出账号</button> <!-- Added export button -->
      </div>

      <div class="field" style="border-left: 1px solid #ccc; padding-left: 15px; margin-left: 10px;">
          <label for="exportCountExcel">导出Excel数量:</label>
          <input id="exportCountExcel" type="number" min="1" max="10000" value="50" style="width: 80px;">
          <button id="exportBtnExcel">导出Excel</button>
      </div>
      <!-- End Export Controls Group -->





      <button id="clearFailedBtn" style="margin-left: 15px;">清空失败账号</button> <!-- Added margin for spacing -->
      <button id="clearUsedBtn">清空Used账号</button>
       <!-- API Status Indicator -->
       <div class="field" style="margin-left: auto;">
           <label>发码服务状态:</label>
           <span id="apiStatus" class="checking" style="font-weight: bold;">检测中...</span>
       </div>
    </div>
    <div id="summary">
      总账号数 (未删除): <span id="acTotal">0</span>
    </div>

    <div id="account-table-container">
      <table id="account-table">
        <thead>
          <tr>
            <th>#</th>
            <th>邮箱</th>
            <th>用户名</th>
            <th>密码</th>
            <th>邀请码</th>
            <th>状态</th>
            <th>错误信息</th>
            <th>操作</th>
          </tr>
        </thead>
        <tbody id="accounts-tbody">
          <!-- Rows will be added by JavaScript -->
          <tr><td colspan="8" style="text-align:center; padding: 20px; color: #888;">加载中...</td></tr>
        </tbody>
      </table>
    </div>

    <div id="pagination">
      <button id="firstPageBtn" disabled>&lt;&lt; 首页</button>
      <button id="prevPageBtn" disabled>&lt; 上一页</button>
      <span id="pageInfo">第 0 页 / 共 0 页</span>
      <button id="nextPageBtn" disabled>下一页 &gt;</button>
      <button id="lastPageBtn" disabled>末页 &gt;&gt;</button>
    </div>

    <div id="log-container">
      <div>过程日志：</div>
      <div id="log">
        <!-- Log messages will be added by JavaScript -->
      </div>
    </div>

  </div>

  <script>
    // --- Configuration ---
    const API_BASE = "/api"; // Assuming Flask runs on the same origin
    const ACCOUNTS_PAGE_SIZE = 20; // Number of accounts per page
    const API_CHECK_INTERVAL = 900000; // Check API status every 30 seconds (ms)

    // --- State Variables ---
    let currentPage = 1;
    let totalPages = 1;
    let totalAccounts = 0;
    let apiCheckIntervalId = null;

    // --- DOM Elements ---
    const tbody = document.getElementById("accounts-tbody");
    const pageInfoSpan = document.getElementById("pageInfo");
    const firstPageBtn = document.getElementById("firstPageBtn");
    const prevPageBtn = document.getElementById("prevPageBtn");
    const nextPageBtn = document.getElementById("nextPageBtn");
    const lastPageBtn = document.getElementById("lastPageBtn");
    const totalAccountsSpan = document.getElementById("acTotal");
    const logDiv = document.getElementById("log");
    const startBatchBtn = document.getElementById("startBatch");
    const regCountInput = document.getElementById("regCount");
    const clearFailedBtn = document.getElementById("clearFailedBtn");
    const clearUsedBtn = document.getElementById("clearUsedBtn");
    const apiStatusSpan = document.getElementById("apiStatus");
    const exportCountInput = document.getElementById("exportCount"); // Add this line
    const exportBtn = document.getElementById("exportBtn");         // Add this line
    const exportBtnExcel = document.getElementById("exportBtnExcel");
    const exportCountExcel = document.getElementById("exportCountExcel");
    // --- Logging Function ---
    function logLine(msg, type = "info") {
        const line = document.createElement("div");
        line.className = `log-line log-${type}`;
        // Basic HTML escaping
        const escapedMsg = msg.replace(/</g, "&lt;").replace(/>/g, "&gt;");
        line.innerHTML = `[${new Date().toLocaleTimeString()}] ${escapedMsg}`;
        logDiv.appendChild(line);
        logDiv.scrollTop = logDiv.scrollHeight; // Auto-scroll to bottom
    }

    // --- API Fetch Helper ---
    async function fetchApi(endpoint, options = {}) {
        try {
            const response = await fetch(API_BASE + endpoint, options);
            if (!response.ok) {
                let errorMsg = `HTTP error! Status: ${response.status}`;
                try {
                    const errData = await response.json();
                    errorMsg += `, Message: ${errData.error || errData.msg || 'Unknown API error'}`;
                } catch (e) {
                    errorMsg += `, Response: ${await response.text()}`;
                }
                throw new Error(errorMsg);
            }
            return await response.json();
        } catch (error) {
            logLine(`API Error (${endpoint}): ${error.message}`, "fail");
            throw error; // Re-throw for calling function to handle
        }
    }

    // --- Account Loading and Rendering ---
    async function loadAccounts(page = 1) {
        logLine(`加载账号列表 (第 ${page} 页)...`, "info");
        tbody.innerHTML = `<tr><td colspan="8" style="text-align:center; padding: 20px; color: #888;">加载中...</td></tr>`;
        try {
            const data = await fetchApi(`/accounts?page=${page}&size=${ACCOUNTS_PAGE_SIZE}`);
            currentPage = data.page;
            totalPages = data.total_pages;
            totalAccounts = data.total;
            renderAccounts(data.data);
            renderPagination();
            totalAccountsSpan.textContent = totalAccounts;
            logLine(`成功加载 ${data.data.length} 个账号`, "success");
        } catch (error) {
            tbody.innerHTML = `<tr><td colspan="8" style="text-align:center; padding: 20px; color: red;">加载账号失败: ${error.message}</td></tr>`;
            // Optionally disable pagination if loading fails
            renderPagination(); // Still render to show disabled state
        }
    }

    function renderAccounts(accounts) {
        tbody.innerHTML = ""; // Clear previous rows
        if (!accounts || accounts.length === 0) {
            tbody.innerHTML = `<tr><td colspan="8" style="text-align:center; padding: 20px; color: #888;">没有账号信息</td></tr>`;
            return;
        }
        accounts.forEach((acc, index) => {
            const tr = document.createElement("tr");
            const statusClass = `status-${acc.status || 'pending'}`;
            const displayIndex = (currentPage - 1) * ACCOUNTS_PAGE_SIZE + index + 1;

            // Basic escaping for display
            const escapeHtml = (unsafe) =>
                unsafe == null ? '' : String(unsafe).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");

            tr.innerHTML = `
                <td>${displayIndex}</td>
                <td title="${escapeHtml(acc.email)}">${escapeHtml(acc.email)}</td>
                <td title="${escapeHtml(acc.username)}">${escapeHtml(acc.username)}</td>
                <td>${escapeHtml(acc.password)}</td>
                <td>${escapeHtml(acc.invite_code)}</td>
                <td class="${statusClass}">${escapeHtml(acc.status)}</td>
                <td>${escapeHtml(acc.err_msg)}</td>
                <td>
                    <button onclick="deleteAccount(${acc.id}, this)" ${acc.is_deleted ? 'disabled' : ''}>
                        ${acc.is_deleted ? '已删除' : '删除'}
                    </button>
                </td>
            `;
            tbody.appendChild(tr);
        });
    }
    function triggerExport() {
      let count;
      try {
          count = parseInt(exportCountInput.value);
          if (isNaN(count) || count < 1) {
              alert("请输入有效的导出数量 (必须大于 0)。");
              exportCountInput.focus();
              return;
          }
          // Optional: Add a max check if desired
          // if (count > 10000) {
          //     alert("导出数量过大 (Max 10000)，请分批导出。");
          //     return;
          // }
      } catch (e) {
          alert("请输入有效的导出数量。");
          exportCountInput.focus();
          return;
      }

      logLine(`尝试导出 ${count} 个成功账号 (格式: 用户名 密码)...`, "info");

      // Construct URL with count query parameter
      const exportUrl = `${API_BASE}/export_accounts?count=${count}`;

      // Navigate to the export endpoint. Browser handles the download.
      window.location.href = exportUrl;
      // Note: Simple navigation doesn't provide easy error handling if the server
      // returns an error instead of a file. More complex fetch logic would be needed
      // to handle server-side errors gracefully before download.
  }

    // --- Pagination ---
    function renderPagination() {
        pageInfoSpan.textContent = `第 ${currentPage} 页 / 共 ${totalPages} 页`;
        firstPageBtn.disabled = currentPage <= 1;
        prevPageBtn.disabled = currentPage <= 1;
        nextPageBtn.disabled = currentPage >= totalPages;
        lastPageBtn.disabled = currentPage >= totalPages;
    }

    function gotoPage(page) {
        if (page >= 1 && page <= totalPages && page !== currentPage) {
            loadAccounts(page);
        }
    }

    // --- Account Actions ---
    async function deleteAccount(id, buttonElement) {
        if (!confirm(`确定要删除账号 ID: ${id} 吗？`)) return;
        buttonElement.disabled = true;
        buttonElement.textContent = '删除中...';
        try {
            await fetchApi(`/account/${id}/delete`, { method: "POST" });
            logLine(`账号 ID: ${id} 已删除`, "warn");
            // Find the row and visually mark as deleted or reload
            const row = buttonElement.closest('tr');
            if (row) {
                 row.style.opacity = '0.5';
                 row.style.textDecoration = 'line-through';
                 buttonElement.textContent = '已删除';
                 // Optionally reload current page after a short delay
                 // setTimeout(() => loadAccounts(currentPage), 1000);
            } else {
                 loadAccounts(currentPage); // Fallback to reload if row not found
            }
        } catch (error) {
            logLine(`删除账号 ID: ${id} 失败: ${error.message}`, "fail");
            buttonElement.disabled = false; // Re-enable button on failure
            buttonElement.textContent = '删除';
            alert(`删除失败: ${error.message}`);
        }
    }

    async function clearAccountsByStatus(status, buttonElement, confirmMsg) {
         if (!confirm(confirmMsg)) return;
         buttonElement.disabled = true;
         buttonElement.textContent = '清理中...';
         const endpoint = status === 'fail' ? '/clear_failed' : '/clear_used';
         try {
            const result = await fetchApi(endpoint, { method: 'POST' });
            logLine(`成功清理 ${result.deleted_count || 0} 个 ${status} 状态的账号`, "warn");
            alert(`成功清理 ${result.deleted_count || 0} 个账号`);
            loadAccounts(1); // Go back to first page after clearing
         } catch (error) {
             logLine(`清理 ${status} 账号失败: ${error.message}`, "fail");
             alert(`清理失败: ${error.message}`);
         } finally {
             buttonElement.disabled = false;
             buttonElement.textContent = status === 'fail' ? '清空失败账号' : '清空Used账号';
         }
    }

    // --- Batch Registration ---
    async function startBatchRegister() {
        let count = parseInt(regCountInput.value);
        if (isNaN(count) || count < 1 || count > 1000) {
            alert("请输入有效的注册数量 (1-1000)");
            return;
        }

        startBatchBtn.disabled = true;
        startBatchBtn.textContent = '注册中...';
        logLine(`开始批量注册 ${count} 个账号...`, "info");

        try {
            const result = await fetchApi('/batch_register', {
                method: "POST",
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ count: count })
            });

            // Log results from the batch process
            (result.logs || []).forEach(line => {
                let type = "info";
                if (line.includes("成功")) type = "success";
                else if (line.includes("失败") || line.includes("Error") || line.includes("错误")) type = "fail";
                else if (line.includes("跳过") || line.includes("警告")) type = "warn";
                logLine(line, type);
            });

            logLine(`批量注册完成。成功: ${result.success}, 失败: ${result.fail}`, "success");
            alert(`批量注册完成。\n成功: ${result.success}\n失败: ${result.fail}`);

        } catch (error) {
            logLine(`批量注册请求失败: ${error.message}`, "fail");
            alert(`批量注册请求失败: ${error.message}`);
        } finally {
            startBatchBtn.disabled = false;
            startBatchBtn.textContent = '开始批量注册';
            loadAccounts(1); // Reload accounts, go to first page
        }
    }

    // --- API Status Check ---
    async function checkApiStatus() {
        logLine("检测发码服务状态...", "info");
        apiStatusSpan.textContent = '检测中...';
        apiStatusSpan.className = 'checking';
        try {
            const result = await fetchApi('/check_send_code');
            if (result.ok) {
                apiStatusSpan.textContent = '可用';
                apiStatusSpan.className = 'ok';
                logLine("发码服务状态: 可用", "success");
            } else {
                apiStatusSpan.textContent = '不可用';
                apiStatusSpan.className = 'error';
                logLine("发码服务状态: 不可用", "warn");
            }
        } catch (error) {
            apiStatusSpan.textContent = '检测失败';
            apiStatusSpan.className = 'error';
            logLine(`发码服务状态检测失败: ${error.message}`, "fail");
        }
    }
    function triggerExportExcel() {
    let count;
    try {
        count = parseInt(exportCountExcel.value);
        if (isNaN(count) || count < 1) {
            alert("请输入有效的导出数量 (必须大于 0)。");
            exportCountExcel.focus();
            return;
        }
        // Optional: Add a max check if desired
        // if (count > 10000) {
        //     alert("导出数量过大 (Max 10000)，请分批导出。");
        //     return;
        // }
    } catch (e) {
        alert("请输入有效的导出数量。");
        exportCountExcel.focus();
        return;
    }

  logLine(`尝试导出 ${count} 个成功账号到Excel文件...`, "info");

  // Construct URL with count query parameter
  const exportUrl = `${API_BASE}/export_accounts_xlsx?count=${count}`;

  // Navigate to the export endpoint. Browser handles the download.
  window.location.href = exportUrl;
  // Note: Simple navigation doesn't provide easy error handling if the server
  // returns an error instead of a file. More complex fetch logic would be needed
  // to handle server-side errors gracefully before download.
}

    // --- Event Listeners ---
    document.addEventListener("DOMContentLoaded", () => {
        // Initial Load
        loadAccounts(1);
        checkApiStatus(); // Initial check
        apiCheckIntervalId = setInterval(checkApiStatus, API_CHECK_INTERVAL); // Periodic check

        // Button Clicks
        startBatchBtn.onclick = startBatchRegister;
        clearFailedBtn.onclick = () => clearAccountsByStatus('fail', clearFailedBtn, '确定要删除所有状态为 "fail" 的账号吗？');
        clearUsedBtn.onclick = () => clearAccountsByStatus('used', clearUsedBtn, '确定要删除所有状态为 "used" 的账号吗？');
        exportBtn.onclick = triggerExport; // Add this line to attach the function
        firstPageBtn.onclick = () => gotoPage(1);
        prevPageBtn.onclick = () => gotoPage(currentPage - 1);
        nextPageBtn.onclick = () => gotoPage(currentPage + 1);
        lastPageBtn.onclick = () => gotoPage(totalPages);
        exportBtnExcel.onclick = triggerExportExcel;
    });

    // Clear interval on page unload (optional)
    window.addEventListener('beforeunload', () => {
        if (apiCheckIntervalId) {
            clearInterval(apiCheckIntervalId);
        }
    });

  </script>
</body>
</html>